package cn.yaoud.salus.imexport.processor;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.io.FileUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFSheet;

import com.alibaba.excel.annotation.ExcelProperty;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import cn.yaoud.salus.imexport.annotation.ParseZdyField;
import cn.yaoud.salus.imexport.utils.OssUtils;
import cn.yaoud.salus.imexport.utils.ZipUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * @author wny
 * @date 2025/2/25 17:44
 * @description 标准导入处理流程
 */
@Data
@Slf4j
public class ExportProcessor {

    /**
     * 导出文件名
     */
    private String fileName;
    /**
     * 分批导出时的文件流存放
     */
    private Map<Integer, Integer> fileCountMap;

    /**
     * 分批导出时的BigExcelWriter存放
     */
    private Map<Integer, BigExcelWriter> writeMap;

    /**
     * 自定义头名称
     */
    private Map<String, String> zdy;

    /**
     * 最终文件对象
     */
    private File file;

    /**
     * oss地址
     */
    // private String ossFilePath;

    /**
     * 是否需要压缩 拆分成多个文件时默认采用压缩
     */
    private Boolean withZip = false;

    /**
     * 是否需要上传oss
     */
    // private Boolean withOssUpload = false;
    /**
     * 上传以及压缩后是否删除源文件
     */
    private Boolean isDeleteSourceFile = true;

    // private Long taskId;

    private Long total;

    private Long processedNum = 0L;

    private static final Integer DEFAULT_MAX_SIZE = 500000;
    private static final Integer SINGLE_DATA_SIZE = 10000;

    public static ExportProcessor init(String fileName) {
        ExportProcessor exportProcessor = new ExportProcessor();
        exportProcessor.setFileName(fileName);
        Map<Integer, Integer> fileCountMap = new HashMap<>();
        fileCountMap.put(1, 0);
        exportProcessor.setFileCountMap(fileCountMap);
        Map<Integer, BigExcelWriter> writeMap = new HashMap<>();
        exportProcessor.setWriteMap(writeMap);
        // 初始化文件存储地址
        String dir = fileName + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        File fileDir = new File("exportAsync" + File.separator + dir);
        fileDir.mkdirs();
        exportProcessor.setFile(fileDir);
        return exportProcessor;
    }

    /**
     * 导出数据
     * 
     * @param data
     *            导出数据集合
     * @param clazz
     *            导出对象的class
     * @param isEnd
     *            是否是最后一行数据，
     * @return
     */
    public BigExcelWriter streamExport(List<?> data, Class clazz) {
        // 默认不传字段的时候，导出对象clazz所有字段
        return streamExport(null, data, clazz, DEFAULT_MAX_SIZE, false);
    }

    /**
     * 导出数据
     *
     * @param exportFields
     *            导出字段
     * @param data
     *            导出数据集合
     * @param clazz
     *            导出对象的class
     * @param singleExcelMaxSize
     *            单个excel最大行数 默认50w
     * @param isEnd
     *            是否是最后一行数据，
     * @return
     */
    private BigExcelWriter streamExport(List<?> data, Class clazz, Integer singleExcelMaxSize, Boolean isEnd) {
        // 默认不传字段的时候，导出对象clazz所有字段
        return streamExport(null, data, clazz, singleExcelMaxSize, isEnd);

    }

    /**
     * 导出数据
     *
     * @param exportFields
     *            导出字段
     * @param data
     *            导出数据集合
     * @param clazz
     *            导出对象的class
     * @param isEnd
     *            是否是最后一行数据，
     * @return
     */
    public BigExcelWriter streamExport(List<String> exportFields, List<?> data, Class clazz) {
        // 默认不传字段的时候，导出对象clazz所有字段
        return streamExport(exportFields, data, clazz, DEFAULT_MAX_SIZE, false);

    }

    /**
     * 导出数据
     *
     * @param exportFields
     *            导出字段
     * @param data
     *            导出数据集合
     * @param clazz
     *            导出对象的class
     * @param singleExcelMaxSize
     *            单个excel最大行数 默认50w
     * @param isEnd
     *            是否是最后一行数据，
     * @return
     */
    private BigExcelWriter streamExport(List<String> exportFields, List<?> data, Class clazz,
        Integer singleExcelMaxSize, Boolean isEnd) {
        if (CollectionUtils.isEmpty(data)) {
            data = new ArrayList<>();
        }
        if (data.size() > SINGLE_DATA_SIZE) {
            log.warn("单次导出数据量超过10000条，当前数据量为{}条", data.size());
            throw new RuntimeException("单次导出数据量超过" + SINGLE_DATA_SIZE + "条");
        }
        BigExcelWriter writer = null;
        String path = this.file.getAbsolutePath() + File.separator;
        if (singleExcelMaxSize > DEFAULT_MAX_SIZE) {
            singleExcelMaxSize = DEFAULT_MAX_SIZE;
        }
        if (data.size() > singleExcelMaxSize) {
            withZip = true;
            // 传入的size已经大于最大值，这个list就已经需要分多个文件写了
            List<? extends List<?>> partition = ListUtils.partition(data, singleExcelMaxSize);
            for (List<?> d : partition) {
                writer = streamExport(exportFields, d, clazz, singleExcelMaxSize, false);
            }
        } else {
            // 获取writer
            writer = getWriter(exportFields, data, clazz, singleExcelMaxSize, path);
        }
        if (isEnd) {
            closeStreamExportWriter(writer);
            // 处理最终的压缩和上传
            processFile();

        }
        return writer;

    }

    private void processFile() {
        if (withZip) {
            File sourceFile = file;
            // 压缩文件
            try {
                ZipUtil.zipDirectory(file.getAbsolutePath(), file.getName() + ".zip");
                this.file = new File(file.getAbsolutePath() + ".zip");
                log.info("压缩文件成功=>{}", file.getAbsolutePath());
            } catch (IOException e) {
                e.printStackTrace();
                log.error("压缩文件异常{}", e.getMessage(), e);
            } finally {
                // 删除原来的文件
                if (isDeleteSourceFile) {
                    deleteFile(sourceFile);
                }
            }
        } else {
            // 不压缩的情况下重新指向文件地址
            File absoluteFile = file.getAbsoluteFile();
            File[] files = absoluteFile.listFiles();
            if (files.length == 1) {
                this.file = files[0];
            } else {
                log.error("文件数量异常{}", files.length);
                throw new RuntimeException("文件数量异常");
            }

        }
        // 上传oss
        /*        if (withOssUpload) {
            // 上传文件
            doUpload();
            if (isDeleteSourceFile) {
                deleteFile(file);
                if (!withZip) {
                    deleteFile(file.getParentFile());
                }
            }
        }*/
    }

    private void deleteFile(File file) {
        try {
            FileUtils.forceDelete(file);
            log.info("删除文件成功=>{}", file.getAbsolutePath());
        } catch (IOException e) {
            log.error("删除文件异常{}", e.getMessage(), e);
        }
    }

    private void doUpload() {
        String uploadFileUrl = OssUtils.uploadFile(file);
        log.info("上传oss成功=>{}", uploadFileUrl);
        // this.setOssFilePath(uploadFileUrl);

    }

    private BigExcelWriter getWriter(List<String> exportFields, List<?> data, Class t, Integer maxSize, String path) {
        // 第一阶段默认只能导出50w条
        if (processedNum > DEFAULT_MAX_SIZE) {
            throw new RuntimeException("导出数据量超过" + DEFAULT_MAX_SIZE + "条");
        }
        if (ObjectUtil.isEmpty(data)) {
            data = new ArrayList<>();
        }
        // 获取当前的writer
        BigExcelWriter writer;
        // 获取当前索引
        int index = fileCountMap.size();
        Integer size = fileCountMap.get(index);
        boolean switchNext = size >= maxSize;
        // 获取剩余可写的行数
        int surplus = maxSize - size;
        if (switchNext) {
            withZip = true;
            // 关闭上一个
            closeStreamExportWriter(writeMap.get(index));
            // 构造下一个
            index = index + 1;
            fileCountMap.put(index, 0);
            writer = buildWriter(path, index, fileName, t, exportFields);
            size = 0;
            surplus = maxSize;
        } else {
            // 获取当前的
            if (writeMap.containsKey(index)) {
                writer = writeMap.get(index);
            } else {
                // 新建
                writer = buildWriter(path, index, fileName, t, exportFields);
            }
        }
        if (data.size() > surplus) {
            // 拆分成两个list
            List<?> subList = data.subList(0, surplus);
            List<?> subList2 = data.subList(surplus, data.size());
            getWriter(exportFields, subList, t, maxSize, path);
            writer = getWriter(exportFields, subList2, t, maxSize, path);
        } else {
            writer.write(data);
            // 计数更新
            int current = data.size() + size;
            fileCountMap.put(index, current);
            // 进度条更新
            processedNum = processedNum + data.size();
            log.info("{}_{}写入数据{}条,当前工作区间共写入{}条，全量已累计写入{}条", fileName, index, data.size(), current, processedNum);
        }

        // updateProcess();
        return writer;
    }

    private void updateProcess() {
        if (false) {
            // 更新进度
            try {
                OssUtils.updateProcess(null, processedNum, total);
                log.info("更新task={}进度条=>{}/{}", null, processedNum, total);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }

    private BigExcelWriter buildWriter(String path, int index, String fileName, Class t, List<String> exportFields) {
        String filedest = String.join("", path, fileName, "_", index + "", ".xlsx");
        BigExcelWriter nextWriter = ExcelUtil.getBigWriter(filedest, fileName);
        // 构造抬头
        buildHead(nextWriter, exportFields, t, zdy);
        // 默认的，未添加alias的属性也会写出，如果想只写出加了别名的字段，可以调用此方法排除之
        nextWriter.setOnlyAlias(true);
        writeMap.put(index, nextWriter);
        return nextWriter;
    }

    private void buildHead(ExcelWriter writer, List<String> exportField, Class t, Map<String, String> zdy) {
        if (CollectionUtils.isEmpty(exportField)) {
            Arrays.stream(t.getDeclaredFields()).filter(field -> null != field.getAnnotation(ExcelProperty.class))
                .forEach(o -> {
                    if (o.isAnnotationPresent(ParseZdyField.class) && null != zdy
                        && ObjectUtil.isNotEmpty(zdy.get(o.getName()))) {
                        writer.addHeaderAlias(o.getName(), zdy.get(o.getName()));
                    } else {
                        writer.addHeaderAlias(o.getName(), o.getAnnotation(ExcelProperty.class).value()[0]);
                    }
                });
        } else {
            // 按前台传入字段顺序
            Map<String,
                String> fields = Arrays.stream(t.getDeclaredFields())
                    .filter(field -> null != field.getAnnotation(ExcelProperty.class))
                    .collect(Collectors.toMap(Field::getName, e -> {
                        if (e.isAnnotationPresent(ParseZdyField.class) && null != zdy
                            && ObjectUtil.isNotEmpty(zdy.get(e.getName()))) {
                            return zdy.get(e.getName());
                        } else {
                            return e.getAnnotation(ExcelProperty.class).value()[0];
                        }
                    }));

            for (String col : exportField) {
                if (fields.containsKey(col)) {
                    writer.addHeaderAlias(col, fields.get(col));
                }
            }
        }
    }

    private void closeStreamExportWriter(BigExcelWriter writer) {
        if (null != writer) {
            // 自适应列宽
            SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();
            Workbook workbook = writer.getWorkbook();

            // 设置数值型的单元格格式
            StyleSet styleSet = writer.getStyleSet();
            CellStyle cellStyleForNumber = styleSet.getCellStyleForNumber();
            cellStyleForNumber.setDataFormat((short)0);
            CellStyle headCellStyle = styleSet.getHeadCellStyle();
            headCellStyle.setFillForegroundColor(IndexedColors.TAN.getIndex());
            // 设置水平居中
            headCellStyle.setAlignment(HorizontalAlignment.CENTER);

            Font headFont = workbook.createFont();
            headFont.setFontHeightInPoints((short)BigDecimal.TEN.intValue());
            headCellStyle.setFont(headFont);

            CellStyle cellStyle = styleSet.getCellStyle();

            cellStyle.setAlignment(HorizontalAlignment.LEFT);

            // 上面需要强转 SXSSFSheet 不然没有 trackAllColumnsForAutoSizing 方法
            // sheet.trackAllColumnsForAutoSizing();
            // 列宽自适应
            sheet.trackAllColumnsForAutoSizing();
            int size = writer.getHeaderAlias().size();
            try {
                for (int i = 0; i < size; i++) {
                    sheet.autoSizeColumn(i);
                }
            } catch (Exception e) {
                log.error("自适应列宽异常", e);
            }
            // 关闭writer，释放内存
            writer.close();

        }
    }

    public void close() {
        // 只需要关闭最后一个
        try {
            closeStreamExportWriter(writeMap.get(writeMap.size()));
            processFile();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
